/**
 * 腾讯云COS操作库
 * 提供文件上传、下载、管理等功能的统一接口
 *
 * 主要功能：
 * 1. 凭证管理：获取不同类型的COS访问凭证（上传、查看、管理、完整权限）
 * 2. COS实例管理：根据凭证初始化COS客户端，支持临时凭证
 * 3. 文件操作：支持小文件简单上传和大文件分片上传
 * 4. 进度监控：实时监控文件操作进度，提供详细的状态反馈
 * 5. 签名URL：生成临时访问URL，支持私有文件访问
 *
 * 技术特性：
 * - 支持任意大小文件上传（自动选择最佳策略）
 * - 完整的错误处理和重试机制
 * - 详细的进度监控和状态反馈
 * - 凭证自动管理和过期检测
 * - 跨平台兼容（uni-app环境）
 *
 * 文档地址: https://cloud.tencent.com/document/product/436/7778
 */

import { myRequest } from './request.js';
import { API } from './config.js';
import COS from 'cos-wx-sdk-v5';
import { generateFilePath } from './tool.js';

// ===== 凭证管理函数 =====

/**
 * 获取文件上传凭证
 * 用于向COS上传文件，包含写入权限
 * 凭证会自动保存到本地存储，避免重复请求
 *
 * 权限范围：
 * - PutObject: 简单上传
 * - PostObject: 表单上传
 * - InitiateMultipartUpload: 初始化分片上传
 * - UploadPart: 上传分片
 * - CompleteMultipartUpload: 完成分片上传
 * - AbortMultipartUpload: 中止分片上传
 * - ListMultipartUploads: 列出分片上传任务
 * - ListParts: 列出已上传分片
 *
 * @returns {Promise<Object>} 上传凭证对象
 * @returns {string} .TmpSecretId - 临时访问密钥ID
 * @returns {string} .TmpSecretKey - 临时访问密钥
 * @returns {string} .SecurityToken - 安全令牌
 * @returns {number} .ExpiredTime - 过期时间戳
 * @returns {string} .Bucket - 存储桶名称
 * @returns {string} .Region - 存储桶地域
 */
export const getUploadCredentials = () => {
    const startTime = Date.now();
    return new Promise((resolve, reject) => {
        myRequest({
            url: API.getUploadCredentials,
            method: 'POST',
            data: {}
        }).then(response => {
            if (response.success) {
                // 保存凭证到本地存储，避免重复请求
                uni.setStorageSync('uploadCredentials', response.data);
                console.log('上传凭证获取成功，耗时:', Date.now() - startTime, 'ms');
                resolve(response.data);
            } else {
                reject(response);
            }
        });
    });
};

/**
 * 获取文件查看凭证
 * 用于从COS下载或查看文件，包含只读权限
 *
 * 权限范围：
 * - GetObject: 获取对象内容
 * - HeadObject: 获取对象元数据
 * - ListBucket: 列出存储桶内容
 * - GetBucketLocation: 获取存储桶位置
 *
 * @returns {Promise<Object>} 查看凭证对象
 */
export const getViewCredentials = () => {
    const startTime = Date.now();
    return new Promise((resolve, reject) => {
        myRequest({
            url: API.getViewCredentials,
            method: 'POST',
            data: {}
        }).then(response => {
            if (response.success) {
                uni.setStorageSync('viewCredentials', response.data);
                console.log('查看凭证获取成功，耗时:', Date.now() - startTime, 'ms');
                resolve(response.data);
            } else {
                reject(response);
            }
        });
    });
};

/**
 * 获取文件管理凭证
 * 用于管理COS中的文件，包含读写权限
 *
 * 权限范围：
 * - 包含上传凭证的所有权限
 * - DeleteObject: 删除对象
 * - CopyObject: 复制对象
 * - PutObjectAcl: 设置对象ACL
 *
 * @returns {Promise<Object>} 管理凭证对象
 */
export const getManageCredentials = () => {
    const startTime = Date.now();
    return new Promise((resolve, reject) => {
        myRequest({
            url: API.getManageCredentials,
            method: 'POST',
            data: {}
        }).then(response => {
            if (response.success) {
                uni.setStorageSync('manageCredentials', response.data);
                console.log('管理凭证获取成功，耗时:', Date.now() - startTime, 'ms');
                resolve(response.data);
            } else {
                reject(response);
            }
        });
    });
};

/**
 * 获取完整权限凭证
 * 包含所有操作权限，用于特殊场景
 *
 * 权限范围：
 * - 包含所有COS操作权限
 * - 适用于管理员或特殊业务场景
 * - 注意：权限过大，请谨慎使用
 *
 * @returns {Promise<Object>} 完整凭证对象
 */
export const getFullCredentials = () => {
    const startTime = Date.now();
    return new Promise((resolve, reject) => {
        myRequest({
            url: API.getFullCredentials,
            method: 'POST',
            data: {}
        }).then(response => {
            if (response.success) {
                uni.setStorageSync('fullCredentials', response.data);
                console.log('完整凭证获取成功，耗时:', Date.now() - startTime, 'ms');
                resolve(response.data);
            } else {
                reject(response);
            }
        });
    });
};

// ===== COS实例管理函数 =====

/**
 * 根据凭证创建COS客户端实例
 * 用于初始化腾讯云COS SDK客户端
 *
 * 功能特性：
 * - 自动验证凭证完整性
 * - 检查凭证是否过期
 * - 配置授权回调函数
 * - 支持临时凭证和永久凭证
 *
 * @param {Object} cosCredentials - COS访问凭证
 * @param {string} cosCredentials.TmpSecretId - 临时访问密钥ID
 * @param {string} cosCredentials.TmpSecretKey - 临时访问密钥
 * @param {string} cosCredentials.SecurityToken - 安全令牌
 * @param {number} cosCredentials.ExpiredTime - 过期时间戳
 * @param {string} cosCredentials.Bucket - 存储桶名称
 * @param {string} cosCredentials.Region - 存储桶地域
 * @returns {Object} COS客户端实例
 * @throws {Error} 当凭证无效或过期时抛出错误
 */
export const createCOSClient = (cosCredentials) => {
    // 验证凭证参数完整性
    if (!cosCredentials) {
        throw new Error('COS凭证参数不能为空');
    }

    if (!cosCredentials.TmpSecretId || !cosCredentials.TmpSecretKey) {
        throw new Error('COS凭证缺少必要信息: TmpSecretId 或 TmpSecretKey');
    }

    if (!cosCredentials.Bucket || !cosCredentials.Region) {
        throw new Error('COS凭证缺少必要信息: Bucket 或 Region');
    }

    // 检查凭证是否过期
    const currentTimestamp = Math.floor(Date.now() / 1000);
    if (cosCredentials.ExpiredTime && currentTimestamp >= cosCredentials.ExpiredTime) {
        throw new Error('COS凭证已过期，请重新获取');
    }

    console.log('创建COS客户端，Bucket:', cosCredentials.Bucket, 'Region:', cosCredentials.Region);

    // 创建COS客户端实例
    const cosClient = new COS({
        getAuthorization: (options, callback) => {
            const authorizationInfo = {
                Bucket: cosCredentials.Bucket,
                Region: cosCredentials.Region,
                TmpSecretId: cosCredentials.TmpSecretId,
                TmpSecretKey: cosCredentials.TmpSecretKey,
                SecurityToken: cosCredentials.SecurityToken,
                ExpiredTime: cosCredentials.ExpiredTime
            };
            callback(authorizationInfo);
        }
    });

    return cosClient;
};

/**
 * 自动获取凭证并创建COS客户端
 * 便捷函数，自动处理凭证获取和客户端创建
 *
 * @returns {Promise<Object>} COS客户端实例
 */
export const createCOSClientAuto = async () => {
    try {
        const uploadCredentials = await getUploadCredentials();
        const cosClient = createCOSClient(uploadCredentials);
        return cosClient;
    } catch (error) {
        console.error('自动创建COS客户端失败:', error);
        throw error;
    }
};

// ===== 文件操作函数 =====

/**
 * 获取文件大小
 * 使用uni-app的getFileInfo API获取本地文件信息
 *
 * @param {string} filePath - 本地文件路径
 * @returns {Promise<number>} 文件大小（字节）
 */
const getFileSize = (filePath) => {
    return new Promise((resolve, reject) => {
        uni.getFileInfo({
            filePath: filePath,
            success: (res) => {
                resolve(res.size);
            },
            fail: (err) => {
                console.error('获取文件信息失败:', err);
                reject(err);
            }
        });
    });
};



/**
 * 小文件上传策略（< 1MB）
 * 使用 putObject 方法，简单高效
 *
 * 适用场景：
 * - 文件大小小于1MB
 * - 网络环境良好
 * - 需要快速上传
 *
 * @param {Object} cosClient - COS客户端
 * @param {Object} options - 上传选项
 * @returns {Promise<Object>} 上传结果
 */
const uploadSmallFile = (cosClient, options) => {
    return new Promise((resolve, reject) => {
        cosClient.putObject({
            Bucket: options.Bucket,
            Region: options.Region,
            Key: options.Key,
            FilePath: options.FilePath,
            onProgress: (progressData) => {
                // 小文件上传的进度处理
                if (options.onProgress) {
                    const progressInfo = {
                        percentage: Math.round((progressData.loaded / progressData.total) * 100),
                        loadedBytes: progressData.loaded,
                        totalBytes: progressData.total,
                        uploadSpeed: progressData.speed,
                        fileName: options.fileName || '小文件',
                        message: `上传中 ${Math.round((progressData.loaded / progressData.total) * 100)}%`
                    };
                    options.onProgress(progressInfo);
                }
            }
        }, (error, result) => {
            if (error) {
                console.error('小文件上传失败:', error);
                reject(error);
            } else {
                resolve(result);
            }
        });
    });
};

/**
 * 大文件分段上传策略（≥ 1MB）
 * 强制使用分片上传，充分利用后端提供的完整权限
 *
 * 技术特性：
 * - 1MB分片大小，3个并发上传
 * - 支持断点续传
 * - 自动重试机制
 * - 详细的进度监控
 *
 * 适用场景：
 * - 文件大小大于等于1MB
 * - 网络环境不稳定
 * - 需要可靠的上传保证
 *
 * @param {Object} cosClient - COS客户端
 * @param {Object} options - 上传选项
 * @returns {Promise<Object>} 上传结果
 */
const uploadLargeFile = (cosClient, options) => {
    return new Promise((resolve, reject) => {
        cosClient.sliceUploadFile({
            Bucket: options.Bucket,
            Region: options.Region,
            Key: options.Key,
            FilePath: options.FilePath,
            ChunkSize: 1024 * 1024, // 1MB 分片大小
            AsyncLimit: 3, // 并发上传分片数
            onProgress: (progressData) => {
                // 分片上传的进度处理
                if (options.onProgress) {
                    const progressInfo = {
                        percentage: Math.round((progressData.loaded / progressData.total) * 100),
                        loadedBytes: progressData.loaded,
                        totalBytes: progressData.total,
                        uploadSpeed: progressData.speed,
                        fileName: options.fileName || '大文件',
                        message: `分片上传中 ${Math.round((progressData.loaded / progressData.total) * 100)}%`
                    };
                    options.onProgress(progressInfo);
                }
            }
        }, (error, result) => {
            if (error) {
                console.error('大文件分片上传失败:', error);
                reject(error);
            } else {
                resolve(result);
            }
        });
    });
};

/**
 * 上传文件到COS
 * 支持任意大小文件上传，自动选择最佳上传策略
 *
 * 上传策略：
 * - 小文件（< 1MB）：使用 putObject 简单上传
 * - 大文件（≥ 1MB）：使用 sliceUploadFile 分片上传
 *
 * 功能特性：
 * - 自动获取文件大小
 * - 智能选择上传策略
 * - 完整的进度监控
 * - 详细的错误处理
 * - 支持自定义凭证
 *
 * @param {Object} uploadOptions - 上传配置选项
 * @param {string} uploadOptions.localFilePath - 本地文件路径
 * @param {string} [uploadOptions.fileType='image'] - 文件类型分类
 * @param {string} [uploadOptions.moduleType='user'] - 业务模块类型
 * @param {Object} [uploadOptions.credentials] - 自定义COS凭证（可选）
 * @param {Function} [uploadOptions.onProgress] - 上传进度回调
 * @param {Function} [uploadOptions.onSuccess] - 上传成功回调
 * @param {Function} [uploadOptions.onError] - 上传失败回调
 * @returns {Promise<Object>} 上传结果
 */
export const uploadFileToCOS = async (uploadOptions) => {
    const {
        localFilePath,
        fileType = 'image',
        moduleType = 'user',
        credentials,
        onProgress,
        onSuccess,
        onError
    } = uploadOptions;

    // 文件大小阈值（1MB）
    const FILE_SIZE_THRESHOLD = 1024 * 1024;

    try {
        // 1. 获取文件大小
        const fileSize = await getFileSize(localFilePath);
        console.log('文件大小:', (fileSize / 1024 / 1024).toFixed(2), 'MB');

        // 2. 获取或使用传入的凭证
        let cosCredentials = credentials;
        if (!cosCredentials) {
            cosCredentials = await getUploadCredentials();
        }

        // 3. 创建COS客户端
        const cosClient = createCOSClient(cosCredentials);

        // 4. 生成COS文件路径
        const filePathInfo = generateFilePath({
            fileName: localFilePath,
            fileType,
            moduleType
        });
        if (!filePathInfo) {
            throw new Error('文件路径生成失败');
        }

        // 5. 准备上传选项
        const uploadOptions = {
            Bucket: cosCredentials.Bucket,
            Region: cosCredentials.Region,
            Key: filePathInfo.fullPath,
            FilePath: localFilePath,
            fileName: filePathInfo.fileName, // 传递文件名用于进度显示
            onProgress: onProgress // 直接传递进度回调
        };

        // 6. 根据文件大小选择上传策略
        let uploadResult;
        if (fileSize < FILE_SIZE_THRESHOLD) {
            // 小文件：使用 putObject 方法，简单高效
            uploadResult = await uploadSmallFile(cosClient, uploadOptions);
        } else {
            // 大文件：强制使用 sliceUploadFile 分片上传，充分利用后端权限
            uploadOptions.fileName = filePathInfo.fileName; // 传递文件名用于进度显示
            uploadResult = await uploadLargeFile(cosClient, uploadOptions);
        }

        // 7. 处理上传成功
        const successResult = {
            fileName: filePathInfo.uniqueFileName, // 唯一文件名
            fullPath: filePathInfo.fullPath, // 完整路径从uploads开始
            fileType: filePathInfo.fileType, // 文件类型
            moduleType: filePathInfo.moduleType, // 模块类型
            extension: filePathInfo.extension, // 文件扩展名
            size: fileSize, // 文件大小
            etag: uploadResult.ETag, // 文件ETag 文件唯一标识
            location: uploadResult.Location // 文件Location包含cos域名
        };

        // 调用成功回调
        onSuccess?.(successResult);
        return successResult;

    } catch (error) {
        console.error('文件上传异常:', error);
        const errorInfo = {
            error,
            message: error.message || '文件上传失败',
            fileName: localFilePath.split('/').pop()
        };

        // 调用错误回调
        onError?.(errorInfo);
        throw errorInfo;
    }
};
/**
 * 根据传递的文件路径和凭证生成可访问的COS签名URL
 * 用于生成私有文件的临时访问链接
 *
 * 功能特性：
 * - 支持自定义有效期
 * - 自动验证凭证有效性
 * - 完整的错误处理
 * - 适用于私有文件访问
 *
 * @param {string} filePath - COS文件路径（从uploads开始的相对路径）
 * @param {Object} credentials - COS访问凭证
 * @param {number} [expires=3600] - 签名URL有效期（秒），默认1小时
 * @returns {Promise<string>} 签名URL
 */
export const getSignedUrl = async (filePath, credentials, expires = 3600) => {
    try {
        // 验证参数
        if (!filePath) {
            throw new Error('文件路径不能为空');
        }

        if (!credentials) {
            throw new Error('COS凭证不能为空');
        }

        // 验证凭证格式
        const requiredFields = ['TmpSecretId', 'TmpSecretKey', 'SecurityToken', 'Bucket', 'Region'];
        for (const field of requiredFields) {
            if (!credentials[field]) {
                throw new Error(`COS凭证缺少必要字段: ${field}`);
            }
        }

        // 检查凭证是否过期
        if (credentials.ExpiredTime && credentials.ExpiredTime * 1000 < new Date().getTime()) {
            throw new Error('COS凭证已过期');
        }

        // 创建COS客户端
        const cosClient = createCOSClient(credentials);

        // 生成签名URL
        return new Promise((resolve, reject) => {
            cosClient.getObjectUrl({
                Bucket: credentials.Bucket,
                Region: credentials.Region,
                Key: filePath,
                Sign: true,
                Expires: expires
            }, (error, data) => {
                if (error) {
                    console.error('生成签名URL失败:', error);
                    reject(new Error(`生成签名URL失败: ${error.message}`));
                } else {
                    resolve(data.Url);
                }
            });
        });

    } catch (error) {
        console.error('生成签名URL异常:', error);
        throw error;
    }
};

// ===== 兼容性导出 =====
// 为了保持向后兼容，保留原有的函数名

/**
 * @deprecated 请使用 createCOSClient 替代
 * 根据凭证创建COS客户端实例
 */
export const initCOSWithCredentials = createCOSClient;

/**
 * @deprecated 请使用 createCOSClientAuto 替代
 * 自动获取凭证并创建COS客户端
 */
export const initCOS = createCOSClientAuto;

/**
 * @deprecated 请使用 uploadFileToCOS 替代
 * 上传文件到COS
 */
export const uploadFile = uploadFileToCOS;

